Intro, a lyrics-melody data frame

library(tidyverse)


beluga_song <- ggdoremi:::join_phrases_drm_lyrics()
## Joining with `by = join_by(drm)`
## Joining with `by = join_by(id_phrase, id_in_phrase)`
beluga_song %>% head()
## # A tibble: 6 × 6
## # Groups:   id_phrase [1]
##   id_phrase id_in_phrase drm   doremi  freq lyric
##       <int>        <int> <fct> <fct>  <dbl> <chr>
## 1         1            1 m     mi      557. Ba   
## 2         1            2 m     mi      557. by   
## 3         1            3 r     re      495  Be   
## 4         1            4 d     do      440  lu   
## 5         1            5 S     sol     330  ga   
## 6         1            6 d     do      440  in

Dandalion song w/o extension

compute_panel_dandalion <- function(data, scales){
  
  spacer_df <- tibble(spacer = 1)
  
  data <- bind_rows(spacer_df, data, spacer_df)
  
  data$id <- 1:nrow(data)
  # nrow(data)
  data$around <- 1:(nrow(data))/nrow(data)
  data$circ_angle <- -data$around*2*pi - 
    pi/2 + 2*pi/nrow(data)/2 
  data$x <- data$radius * cos(data$circ_angle)
  data$xend <- 0
  data$yend <- 0
  data$y <- data$radius * sin(data$circ_angle)
  data$angle <- -data$around*360 + 180
  
  data %>% 
    slice(-1) %>% 
    slice(-nrow(.))
  
}

ggdoremi:::join_phrases_drm_lyrics() %>% 
  # head(30) %>% 
  mutate(radius = as.numeric(drm) + 10) %>% 
  compute_panel_dandalion() %>% 
  ggplot() + 
  ggstamp::stamp_circle(radius = 8+10, alpha = 0, size = .25, linetype = "dotted") +
  ggstamp::stamp_circle(radius = 8+12, alpha = 0, size = .25, linetype = "dotted") +
  ggstamp::stamp_circle(radius = 8+14, alpha = 0, size = .25, linetype = "dotted") +
  aes(x = x, radius = radius, y = y, label = lyric, angle = angle) + 
  geom_segment(xend = 0, yend = 0, color = "black", linewidth = .5) +
  geom_point(shape = 21, size = 16, fill = "white") +
  # aes(color = doremi) +
  geom_text() +
  coord_equal()  + 
  theme_void()
## Joining with `by = join_by(drm)`
## Joining with `by = join_by(id_phrase, id_in_phrase)`
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_segment()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_text()`).

statexpress enabled extension

write extension

stat_dandalion <- function(geom = "segment", ...){
  statexpress::stat_panel(compute_panel_dandalion, geom, ...)
}

geom_dandalion_segment <- stat_dandalion
geom_dandalion_text <- function(...){stat_dandalion(geom =  "text", ...)}
geom_dandalion_point <- function(...){stat_dandalion(geom =  "point", ...)}
stamp_staff_dms <- function(radius_addition = 10 ){
  
  list(
  ggstamp::stamp_circle(radius = 8+0+radius_addition, alpha = 0, size = .25, linetype = "dotted"),
  ggstamp::stamp_circle(radius = 8+2+radius_addition, alpha = 0, size = .25, linetype = "dotted"),
  ggstamp::stamp_circle(radius = 8+4+radius_addition, alpha = 0, size = .25, linetype = "dotted")
  )
  
  
}


stamp_staff_dmst <- function(radius_addition = 10 ){
  
  list(stamp_staff_dms(radius_addition = radius_addition), 
      ggstamp::stamp_circle(radius = 8+6+radius_addition, 
                            alpha = 0, size = .25, 
                            linetype = "dotted")
  )
  
  
}

stamp_staff_dmstr <- function(radius_addition = 10 ){
  
  list(stamp_staff_dmst(radius_addition = radius_addition), 
      ggstamp::stamp_circle(radius = 8+8+radius_addition, 
                            alpha = 0, size = .25, 
                            linetype = "dotted")
  )
  
  
}

Use extension

ggdoremi:::join_phrases_drm_lyrics() ->
beluga_song
## Joining with `by = join_by(drm)`
## Joining with `by = join_by(id_phrase, id_in_phrase)`
head(beluga_song) 
## # A tibble: 6 × 6
## # Groups:   id_phrase [1]
##   id_phrase id_in_phrase drm   doremi  freq lyric
##       <int>        <int> <fct> <fct>  <dbl> <chr>
## 1         1            1 m     mi      557. Ba   
## 2         1            2 m     mi      557. by   
## 3         1            3 r     re      495  Be   
## 4         1            4 d     do      440  lu   
## 5         1            5 S     sol     330  ga   
## 6         1            6 d     do      440  in
beluga_song %>% 
  ggplot() + 
  aes(radius = as.numeric(drm)) +
  geom_dandalion_segment() + 
  geom_dandalion_point(size = 16, 
                       shape = 21,
                       fill = "white") +
  aes(label = lyric) +
  geom_dandalion_text() + 
  coord_equal()
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_segment()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_text()`).

Another example

lyrics_abc <- "a b c d e f g
h i j k l m n o p
q r s t u v
w x y z
Now I know my A B Cs
Next time won't you sing with me"


drm_abc <- "ddsslls
ffmmrrrrd
ssfmmr
sfmr
ddsslls
ffmmrrd"


ggdoremi:::join_phrases_drm_lyrics(drm_abc, 
                                   lyrics_phrases = 
                                     lyrics_abc %>% str_to_title()) %>% 
  ggplot() + 
  aes(radius = as.numeric(drm) + 3) +
  stamp_staff_dms(3) +
  geom_dandalion_segment(color = "black", 
                         size = .5) +
  geom_dandalion_point(size = 10, 
                       fill = "white",
                       shape = 21) +
  aes(label = lyric) +
  geom_dandalion_text() +
  coord_equal() + 
  theme_void() + 
  aes(size = 5)
## Joining with `by = join_by(drm)`
## Joining with `by = join_by(id_phrase, id_in_phrase)`
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

last_plot() %+%
  (ggdoremi:::join_phrases_drm_lyrics(drm_abc, 
                                   lyrics_abc)[1:26,]) + 
  aes(size = 7) + 
  guides("none")
## Joining with `by = join_by(drm)`
## Joining with `by = join_by(id_phrase, id_in_phrase)`
## Warning: Guides provided to `guides()` must be named.
## ℹ All guides are unnamed.

lyrics_doe_a_deer <-
'Doe a deer a fe-male deer
ray a drop of gol-den sun
me a name I call my-self
far a long long way to run
sew a need-le pull-ing thread
la a note to fol-low sol
tea a drink with jam and bread
that will bring us back to do o o o'

drm_doe_a_deer <-
'drmrmrm
rmffmrf
mfsmsms
fsllsfl
sdrmfsl
lrmfslt
tmfslt1
1tlsts1smr'


ggdoremi:::join_phrases_drm_lyrics(drm_doe_a_deer, lyrics_phrases = lyrics_doe_a_deer) %>%
  # head(30) %>%
  ggplot() +
  stamp_staff_dmst(0) +
  aes(radius = as.numeric(drm) ) +
  geom_dandalion_segment(xend = 0, yend = 0) +
  geom_dandalion_point(shape = 21, size = 19, fill = "white") +
  aes(color = doremi, label = lyric) +
  geom_dandalion_text(size = 7) +
  coord_equal() + 
  theme_void() + 
  guides(color = "none", label = "none")
## Joining with `by = join_by(drm)`
## Joining with `by = join_by(id_phrase, id_in_phrase)`

last_plot() + 
  aes(label = doremi)

Closing remarks, Other Relevant Work, Caveats